#include <gmlib/cipher/mode.h>
#include <gmlib/cipher/sm4.h>
#include <gmlib/err.h>
#include <memory.h>
#include <stdlib.h>

static uint8_t key[SM4_KEYLEN] = {
    0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF,  //
    0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,  //
};

static uint8_t iv[SM4_KEYLEN] = {
    0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF,  //
    0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,  //
};

// 测试点1

static uint8_t pt1[] = {
    0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF,  //
    0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,  //
    0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF,  //
    0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,  //
    0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF,  //
    0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,  //
};

static uint8_t ct1[] = {
    0x26, 0x77, 0xF4, 0x6B, 0x09, 0xC1, 0x22, 0xCC,  //
    0x97, 0x55, 0x33, 0x10, 0x5B, 0xD4, 0xA2, 0x2A,  //
    0xF6, 0x12, 0x5F, 0x72, 0x75, 0xCE, 0x55, 0x2C,  //
    0x3A, 0x2B, 0xBC, 0xF5, 0x33, 0xDE, 0x8A, 0x3B,  //
    0xFF, 0xF5, 0xA4, 0xF2, 0x08, 0x09, 0x2C, 0x09,  //
    0x01, 0xBA, 0x02, 0xD5, 0x77, 0x29, 0x77, 0x36,  //
    0x76, 0x71, 0x3D, 0xC4, 0x0B, 0x7F, 0x14, 0xE5,  //
    0x0B, 0x64, 0x8D, 0x1C, 0x74, 0x3F, 0xE6, 0xAF,  //
};

static uint8_t pt2[] = {
    0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF,  //
    0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54,              //
};

static uint8_t ct2[] = {
    0x97, 0x8B, 0xA5, 0xCC, 0xF7, 0x68, 0xAB, 0x0F,  //
    0x11, 0x16, 0x40, 0xCC, 0x8E, 0x94, 0xE3, 0x2A,  //
};

static uint8_t out[SM4_BLOCK_SIZE * 4];
static int outl;

void test_mode_cbc() {
    SM4_CTX sm4_ctx;
    CBC_CTX ctx;
    uint8_t* outptr;
    // 测试点1
    cbc_init(key, iv, &SM4Info, &sm4_ctx, &ctx);
    outptr = out;
    cbc_encrypt_update(outptr, &outl, pt1, sizeof(pt1), &ctx);
    outptr += outl;
    cbc_encrypt_final(outptr, &outl, &ctx);
    outptr += outl;
    if (memcmp(out, ct1, sizeof(ct1)) != 0 || outptr - out != sizeof(ct1)) {
        ERR_LOG("Err in cbc mode");
        goto error;
    }
    // 测试点2
    cbc_init(key, iv, &SM4Info, &sm4_ctx, &ctx);
    outptr = out;
    cbc_encrypt_update(outptr, &outl, pt2, sizeof(pt2), &ctx);
    outptr += outl;
    cbc_encrypt_final(outptr, &outl, &ctx);
    outptr += outl;
    if (memcmp(out, ct2, sizeof(ct2)) != 0 || outptr - out != sizeof(ct2)) {
        ERR_LOG("Err in cbc mode");
        goto error;
    }
    // 测试解密
    cbc_init(key, iv, &SM4Info, &sm4_ctx, &ctx);
    outptr = out;
    cbc_decrypt_update(outptr, &outl, ct2, sizeof(ct2), &ctx);
    outptr += outl;
    try_goto(cbc_decrypt_final(outptr, &outl, &ctx));
    outptr += outl;
    if (memcmp(out, pt2, sizeof(pt2)) != 0 || outptr - out != sizeof(pt2)) {
        ERR_LOG("Err in cbc mode");
        goto error;
    }

    return;
error:
    exit(-1);
}
